﻿using ArcSoftFace.Entity;
using ArcSoftFace.SDKModels;
using ArcSoftFace.SDKUtil;
using ArcSoftFace.Utils;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace ArcSoftFace
{
    /// <summary>
    /// 暂时只开启了RGB活体检测
    /// </summary>
    public class FaceCompare
    {
        private static string message = ""; //消息,跨线程
        private static float threshold = 0.8f; //相似度阈值
        private static bool isRGBLock = false;
        private static object rectLock = new object(); //代码锁
        private static MRECT allRect = new MRECT(); //人脸框信息

        /// <summary>
        /// 左侧图库人脸特征列表
        /// </summary>
        private static List<IntPtr> imagesFeatureList = new List<IntPtr>();

        /// <summary>
        /// RGB视频引擎 FR Handle 处理   FR和图片引擎分开，减少强占引擎的问题
        /// </summary>
        private static IntPtr pVideoRGBImageEngine = IntPtr.Zero;

        /// <summary>
        /// IR视频引擎 FR Handle 处理   FR和图片引擎分开，减少强占引擎的问题
        /// </summary>
        private static IntPtr pVideoIRImageEngine = IntPtr.Zero;

        /// <summary>
        /// 视频引擎Handle
        /// </summary>
        private static IntPtr pVideoEngine = IntPtr.Zero;

        /// <summary>
        /// 引擎Handle
        /// </summary>
        private static IntPtr pImageEngine = IntPtr.Zero;


        /// <summary>
        /// 初始化引擎
        /// </summary>
        /// <returns>返回"1"表示成功,否则表示失败</returns>
        public static string InitEngines()
        {
            //读取配置文件
            AppSettingsReader reader = new AppSettingsReader();
            string appId = (string)reader.GetValue("APP_ID", typeof(string));
            string sdkKey64 = (string)reader.GetValue("SDKKEY64", typeof(string));
            string sdkKey32 = (string)reader.GetValue("SDKKEY32", typeof(string));
            //判断CPU位数
            var is64CPU = Environment.Is64BitProcess;
            if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(is64CPU ? sdkKey64 : sdkKey32))
            {
                return string.Format("请在App.config配置文件中先配置APP_ID和SDKKEY{0}!", is64CPU ? "64" : "32");
            }

            //在线激活引擎    如出现错误，1.请先确认从官网下载的sdk库已放到对应的bin中，2.当前选择的CPU为x86或者x64
            int retCode = 0;
            try
            {
                retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32);
            }
            catch (Exception ex)
            {
                //禁用相关功能按钮
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    return "请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!";
                }
                else
                {
                    return "激活引擎失败!";
                }
            }
            Console.WriteLine("Activate Result:" + retCode);

            //初始化引擎
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
            //Video模式下检测脸部的角度优先值
            int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
            //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
            //人脸在图片中所占比例，如果需要调整检测人脸尺寸请修改此值，有效数值为2-32
            int detectFaceScaleVal = 16;
            //最大需要检测的人脸个数
            int detectFaceMaxNum = 5;
            //引擎初始化时需要初始化的检测功能组合
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
            //初始化引擎，正常值为0，其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
            Console.WriteLine("InitEngine Result:" + retCode);
            //AppendText((retCode == 0) ? "引擎初始化成功!\n" : string.Format("引擎初始化失败!错误码为:{0}\n", retCode));
            if (retCode != 0)
            {
                //禁用相关功能按钮
                return string.Format("引擎初始化失败!错误码为:{0}\n", retCode);
            }

            //初始化视频模式下人脸检测引擎
            uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
            int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
            retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
            //RGB视频专用FR引擎
            detectFaceMaxNum = 1;
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);

            //IR视频专用FR引擎
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_IR_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoIRImageEngine);

            Console.WriteLine("InitVideoEngine Result:" + retCode);
            return "1";
        }

        /// <summary>
        /// 传入一张摄像头获取的人脸图片,与特征库中的图片进行比较,返回比较的结果
        /// 含RBG活体检测
        /// </summary>
        /// <param name="bitmap">摄像头拍摄的图片</param>
        /// <param name="rgbVideoSourceWidth">摄像头镜头的宽度(程序中视频的宽度)</param>
        /// <param name="rgbVideoSourceHeight">摄像头镜头的高度(程序中视频的高度)</param>
        /// <returns></returns>
        public static string DoRgbCompare(Bitmap bitmap, int rgbVideoSourceWidth, int rgbVideoSourceHeight)
        {
            if (bitmap == null)
            {
                return "图片为空";
            }
            //检测人脸，得到Rect框
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
            //得到最大人脸
            ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
            //得到Rect
            MRECT rect = maxFace.faceRect;
            //检测RGB摄像头下最大人脸
            //Graphics g = e.Graphics;
            float offsetX = rgbVideoSourceWidth * 1f / bitmap.Width;
            float offsetY = rgbVideoSourceHeight * 1f / bitmap.Height;
            float x = rect.left * offsetX;
            float width = rect.right * offsetX - x;
            float y = rect.top * offsetY;
            float height = rect.bottom * offsetY - y;
            //根据Rect进行画框

            //保证只检测一帧，防止页面卡顿以及出现其他内存被占用情况
            if (isRGBLock == false)
            {
                isRGBLock = true;
                //异步处理提取特征值和比对，不然页面会比较卡
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                {
                    if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)
                    {
                        try
                        {
                            lock (rectLock)
                            {
                                allRect.left = (int)(rect.left * offsetX);
                                allRect.top = (int)(rect.top * offsetY);
                                allRect.right = (int)(rect.right * offsetX);
                                allRect.bottom = (int)(rect.bottom * offsetY);
                            }

                            bool isLiveness = false;

                            //调整图片数据，非常重要
                            ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
                            if (imageInfo == null)
                            {
                                message = "ImageInfoIsNull";
                                return;
                            }
                            int retCode_Liveness = -1;
                            //RGB活体检测
                            ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness);
                            //判断检测结果
                            if (retCode_Liveness == 0 && liveInfo.num > 0)
                            {
                                int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
                                isLiveness = (isLive == 1) ? true : false;
                            }
                            if (imageInfo != null)
                            {
                                MemoryUtil.Free(imageInfo.imgData);
                            }
                            if (isLiveness)
                            {
                                //提取人脸特征
                                IntPtr feature = FaceUtil.ExtractFeature(pVideoRGBImageEngine, bitmap, maxFace);
                                float similarity = 0f;
                                //得到比对结果
                                int result = CompareFeature(feature, out similarity);
                                MemoryUtil.Free(feature);
                                if (result > -1)
                                {
                                    //将比对结果放到显示消息中，用于最新显示
                                    message = $"RGB活体,{result}号,相识度:{similarity}";
                                }
                                else
                                {
                                    //显示消息
                                    message = $"RGB活体,-1,相识度:{similarity}";
                                }
                            }
                            else
                            {
                                //显示消息
                                message = string.Format("RGB假体");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                        finally
                        {
                            if (bitmap != null)
                            {
                                bitmap.Dispose();
                            }
                            isRGBLock = false;
                        }
                    }
                    else
                    {
                        //显示消息
                        message = string.Format("没有人脸");
                        lock (rectLock)
                        {
                            allRect.left = 0;
                            allRect.top = 0;
                            allRect.right = 0;
                            allRect.bottom = 0;
                        }
                    }
                    isRGBLock = false;
                }));
            }

            return message;
        }

        /// <summary>
        /// 得到feature比较结果
        /// </summary>
        /// <param name="feature">传入摄像头获取的图片的人脸特征值</param>
        /// <param name="similarity">出参,返回相似度</param>
        /// <returns>匹配到的图片在特征库中的位置</returns>
        private static int CompareFeature(IntPtr feature, out float similarity)
        {
            int result = -1;
            similarity = 0f;
            //如果人脸库不为空，则进行人脸匹配
            if (imagesFeatureList != null && imagesFeatureList.Count > 0)
            {
                for (int i = 0; i < imagesFeatureList.Count; i++)
                {
                    //调用人脸匹配方法，进行匹配
                    ASFFunctions.ASFFaceFeatureCompare(pVideoRGBImageEngine, feature, imagesFeatureList[i], ref similarity);
                    if (similarity >= threshold)
                    {
                        result = i;
                        break;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 注册一张人脸照片到特征库中
        /// 如果返回IntPtr.Zero就是注册失败,有可能是提取人脸特征失败(没有人脸,人脸太小,太模糊等原因)
        /// </summary>
        /// <param name="image">待注册的张人脸照片</param>
        /// <returns></returns>
        public static string RegisterImage(Image image)
        {
            if (image == null)
            {
                return "图片为空";
            }
            if (image.Width > 1536 || image.Height > 1536)
            {
                image = ImageUtil.ScaleImage(image, 1536, 1536);
            }
            if (image == null)
            {
                return "图片缩放后为空";
            }
            if (image.Width % 4 != 0)
            {
                image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
            }
            //人脸检测
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
            //判断检测结果
            if (multiFaceInfo.faceNum > 0)
            {
                // 存在人脸
                //imagePathList.Add(imagePathListTemp[i]);
                MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
                image = ImageUtil.CutImage(image, rect.left, rect.top, rect.right, rect.bottom);
            }
            else
            {
                //图片中没有人脸
                return "图片中没有人脸";
            }

            ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
            IntPtr feature = FaceUtil.ExtractFeature(pImageEngine, image, out singleFaceInfo);
            if (singleFaceInfo.faceRect.left == 0 && singleFaceInfo.faceRect.right == 0)
            {
                //AppendText(string.Format("{0}号未检测到人脸\r\n", i));
                return "特征值提取失败:未检测到人脸";
            }
            else
            {
                imagesFeatureList.Add(feature);
                return "1";
                // AppendText(string.Format("已提取{0}号人脸特征值，[left:{1},right:{2},top:{3},bottom:{4},orient:{5}]\r\n", i, singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.right, singleFaceInfo.faceRect.top, singleFaceInfo.faceRect.bottom, singleFaceInfo.faceOrient));
                //imagesFeatureList.Add(feature);
            }
        }

    }
}
